﻿using OpenCvSharp;
using OpenCvSharp.Extensions;
using OpenCvSharp.Features2D;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Forms;
using static 摸鱼._数据库._方案;
using static 摸鱼._数据库._方案._步骤;
using static 摸鱼.全局类;
using static 摸鱼.核心;
using static 摸鱼.核心._数据;
using Mat = OpenCvSharp.Mat;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;

namespace 摸鱼
{
    public static class 图形处理
    {
        public static void 图片录制拼接(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            int 帧间隔 = 方法.参数[CS.识图帧数][valueType.Int];
            float 相似度 = 方法.参数[CS.相似度1][valueType.Float];
            Point 中心点by屏幕 = 方法.参数[CS.坐标].opencv坐标;
            int 录制半径 = 方法.参数[CS.半径][valueType.Int];
            int 抠图半径 = 方法.参数[CS.数字1][valueType.Int];
            var 图片显示位置 = 方法.参数[CS.区域2].区域opencvRect;
            int 小地图宽度 = 录制半径 * 2;
            int 小地图高度 = 录制半径 * 2;
            Point 中心点by小图 = new(录制半径, 录制半径);
            OpenCvSharp.Rect 录制区域 = new OpenCvSharp.Rect(中心点by屏幕.X - 录制半径, 中心点by屏幕.Y - 录制半径, 小地图宽度, 小地图高度);

            int 初始画布宽度 = 小地图宽度 * 5;
            int 初始画布高度 = 小地图高度 * 5;

            // 使用白色背景
            OpenCvSharp.Mat 大图 = new OpenCvSharp.Mat(初始画布高度, 初始画布宽度, OpenCvSharp.MatType.CV_8UC4, new OpenCvSharp.Scalar(255, 255, 255, 255));

            // 中心点从画布中心开始
            int 中心X = 初始画布宽度 / 2 - 小地图宽度 / 2;
            int 中心Y = 初始画布高度 / 2 - 小地图高度 / 2;

            int 帧计数 = 0;
            (int X, int Y, double 匹配度) 匹配位置 = default;
            // 创建定时器录制帧
            方法data.信号.Timer((Action)(() =>
            {
                var frame = new OpenCvSharp.Mat(截屏.图片[0, imageType.Mat], 录制区域);
                //Cv2.ImShow("frame", frame);
                //Cv2.WaitKey(0);
                try
                {
                    Cv2.CvtColor(frame, frame, ColorConversionCodes.BGRA2BGR);
                    帧计数++;
                    if (帧计数 == 1)
                    {
                        放置帧到位置(大图, frame, 中心X, 中心Y);
                        方法data.控件 = new();
                        Thread thread = new(() =>
                        {
                            var 实例 = 图片显示器.显示图片在位置(大图, 图片显示位置.X, 图片显示位置.Y, 图片显示位置.Width, 图片显示位置.Height);
                            while (方法data?.控件 != null)
                            {
                                图片显示器.显示图片(大图);
                                实例.TopMost = true;
                                System.Windows.Forms.Application.DoEvents();
                                Thread.Sleep(100);
                            }
                            实例.Dispose();
                            大图.Dispose();
                        });
                        thread.IsBackground = true;
                        thread.Start();
                    }
                    else
                    {
                        // 在完整地图中寻找当前帧的最佳匹配位置
                        匹配位置 = 图片拼接找图(大图, frame);
                        if (匹配位置.匹配度 > 相似度)
                            放置帧到位置(大图, frame, 匹配位置.X, 匹配位置.Y);
                        else
                            Console.WriteLine($"匹配度太低 ({匹配位置.匹配度:F3})，跳过该帧");
                    }

                    // 检查是否需要扩展画布
                    if (帧计数 % 3 == 0)
                        大图 = 检查并扩展画布(大图, 小地图宽度, 小地图高度, 匹配位置);

                    if (方法Action.键盘按键判断(数据, 步骤, 方法, 方法data))
                        方法data.信号.ExitTimer();
                }
                catch (Exception ex)
                {
                    frame.Dispose();
                    Console.WriteLine($"处理帧时出错: {ex.Message}");
                }
                finally
                {
                    frame.Dispose();
                }
            }), 帧间隔);

            // 最终保存画布
            Cv2.ImWrite("debug_final_canvas.jpg", 大图);
            // 将白色背景改为黑色
            替换背景颜色(大图, new Scalar(255, 255, 255), new Scalar(0, 0, 0));
            // 裁剪并保存
            OpenCvSharp.Mat 最终地图 = 裁剪黑色区域(大图);
            Cv2.CvtColor(最终地图, 最终地图, ColorConversionCodes.BGR2BGRA);

            if (!最终地图.Empty())
            {
                方法.参数[CS.图片1].值.覆盖图片(new List<MoyuBitmap>([new MoyuBitmap(最终地图)]));
                最终地图.Dispose();
            }
        }
        public static void 图片录制拼接a(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            int 帧间隔 = 方法.参数[CS.识图帧数][valueType.Int];
            int[] 区域 = 方法.参数[CS.区域].区域s;
            float 相似度 = 方法.参数[CS.相似度1][valueType.Float];
            OpenCvSharp.Rect 录制区域 = new OpenCvSharp.Rect(区域[0], 区域[1], 区域[2] - 区域[0], 区域[3] - 区域[1]);

            List<MoyuBitmap> bitmaps = 方法.参数[CS.图片1].值.取图();
            HashSet<Scalar> 颜色集合 = new HashSet<Scalar>();
            foreach (var bitmap in bitmaps)
            {
                var mat = bitmap.Mat;
                int width = mat.Width;
                int height = mat.Height;
                // 遍历图片中的所有像素来收集颜色
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                        颜色集合.Add(mat.GetScalar(x, y));
                }
            }
            Rect 找色区域 = 方法.参数[CS.区域].区域opencvRect;
            float 色相精度 = 1 - 方法.参数[CS.H][valueType.Float];
            float 饱和度精度 = 1 - 方法.参数[CS.S][valueType.Float];
            float 亮度精度 = 1 - 方法.参数[CS.V][valueType.Float];
            int 边缘膨胀大小 = 方法.参数[CS.数字1][valueType.Int];

            int 小地图宽度 = 录制区域.Width;
            int 小地图高度 = 录制区域.Height;

            // 初始画布为小图的2倍
            int 初始画布宽度 = 小地图宽度 * 2;
            int 初始画布高度 = 小地图高度 * 2;

            // 使用白色背景
            OpenCvSharp.Mat 大图 = new OpenCvSharp.Mat(初始画布高度, 初始画布宽度, OpenCvSharp.MatType.CV_8UC3, new OpenCvSharp.Scalar(255, 255, 255));

            // 中心点从画布中心开始
            int 中心X = 初始画布宽度 / 2 - 小地图宽度 / 2;
            int 中心Y = 初始画布高度 / 2 - 小地图高度 / 2;

            int 帧计数 = 0;
            (int X, int Y, double 匹配度) 匹配位置 = default;
            // 创建定时器录制帧
            方法data.信号.Timer((Action)(() =>
            {
                try
                {
                    using var frame = new OpenCvSharp.Mat(截屏.图片[0, imageType.Mat], 录制区域);
                    Cv2.CvtColor(frame, frame, ColorConversionCodes.BGRA2BGR);

                    帧计数++;
                    if (帧计数 == 1)
                    {
                        放置帧到位置(大图, frame, 中心X, 中心Y);
                        方法data.控件 = new();
                        Thread thread = new(() =>
                        {
                            var 实例 = 图片显示器.显示图片(大图);
                            while (方法data?.控件 != null)
                            {
                                图片显示器.显示图片(大图);
                                实例.TopMost = true;
                                System.Windows.Forms.Application.DoEvents();
                                Thread.Sleep(100);
                            }
                            实例.Dispose();
                            大图.Dispose();
                        });
                        thread.IsBackground = true;
                        thread.Start();
                    }
                    else
                    {
                        // 在完整地图中寻找当前帧的最佳匹配位置
                        // 匹配位置 = 图片拼接找图(大图, frame);
                        if (匹配位置.匹配度 > 相似度)
                            放置帧到位置(大图, frame, 匹配位置.X, 匹配位置.Y);
                        else
                            Console.WriteLine($"匹配度太低 ({匹配位置.匹配度:F3})，跳过该帧");
                    }

                    // 检查是否需要扩展画布
                    if (帧计数 % 3 == 0)
                        大图 = 检查并扩展画布(大图, 小地图宽度, 小地图高度, 匹配位置);

                    if (方法Action.键盘按键判断(数据, 步骤, 方法, 方法data))
                        方法data.信号.ExitTimer();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"处理帧时出错: {ex.Message}");
                }
            }), 帧间隔);

            // 最终保存画布
            Cv2.ImWrite("debug_final_canvas.jpg", 大图);
            // 将白色背景改为黑色
            替换背景颜色(大图, new Scalar(255, 255, 255), new Scalar(0, 0, 0));
            // 裁剪并保存
            OpenCvSharp.Mat 最终地图 = 裁剪黑色区域(大图);
            Cv2.CvtColor(最终地图, 最终地图, ColorConversionCodes.BGR2BGRA);

            if (!最终地图.Empty())
            {
                方法.参数[CS.图片1].值.覆盖图片(new List<MoyuBitmap>([new MoyuBitmap(最终地图)]));
                最终地图.Dispose();
            }
        }
        //private static (int X, int Y, double 匹配度) 图片拼接找色(Mat 大图, HashSet<Scalar> 颜色集合, Rect 找色区域, float 色相精度, float 饱和度精度, float 亮度精度, int 边缘膨胀大小)
        //{
        //    Using(大图, 大图 =>
        //    {
        //        // 提取找色区域
        //        Mat 区域图像 = new Mat(大图, 找色区域);

        //        // 转换为HSV颜色空间
        //        Mat hsv图像 = new Mat();
        //        Cv2.CvtColor(区域图像, hsv图像, ColorConversionCodes.BGR2HSV);

        //        // 存储每个颜色的掩码
        //        List<Mat> 颜色掩码列表 = new List<Mat>();
        //        foreach (Scalar 目标颜色 in 颜色集合)
        //        {
        //            // 将目标BGR颜色转换为HSV
        //            Mat 目标颜色Mat = new Mat(1, 1, MatType.CV_8UC3, 目标颜色);
        //            Mat 目标颜色HSV = new Mat();
        //            Cv2.CvtColor(目标颜色Mat, 目标颜色HSV, ColorConversionCodes.BGR2HSV);
        //            Vec3b hsv值 = 目标颜色HSV.At<Vec3b>(0, 0);

        //            // 根据精度设置颜色范围
        //            int h范围 = (int)(色相精度 * 180);
        //            int s范围 = (int)(饱和度精度 * 255);
        //            int v范围 = (int)(亮度精度 * 255);

        //            Scalar 下限 = new Scalar(
        //                Math.Max(0, hsv值.Item0 - h范围),
        //                Math.Max(0, hsv值.Item1 - s范围),
        //                Math.Max(0, hsv值.Item2 - v范围)
        //            );
        //            Scalar 上限 = new Scalar(
        //                Math.Min(180, hsv值.Item0 + h范围),
        //                Math.Min(255, hsv值.Item1 + s范围),
        //                Math.Min(255, hsv值.Item2 + v范围)
        //            );

        //            Mat 颜色掩码 = new Mat();
        //            Cv2.InRange(hsv图像, 下限, 上限, 颜色掩码);

        //            颜色掩码列表.Add(颜色掩码);
        //        }

        //        // 使用轮廓交集
        //        List<List<Rect>> 所有颜色的轮廓列表 = new List<List<Rect>>();
        //        foreach (var 颜色掩码 in 颜色掩码列表)
        //        {
        //            Point[][] 轮廓数组;
        //            HierarchyIndex[] 层级数组;
        //            Cv2.FindContours(颜色掩码, out 轮廓数组, out 层级数组, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

        //            List<Rect> 当前颜色轮廓 = new List<Rect>();
        //            foreach (var 轮廓 in 轮廓数组)
        //            {
        //                double 轮廓面积 = Cv2.ContourArea(轮廓);
        //                Rect 边界矩形 = Cv2.BoundingRect(轮廓);
        //                当前颜色轮廓.Add(边界矩形);
        //            }
        //            所有颜色的轮廓列表.Add(当前颜色轮廓);
        //        }

        //        // 找到包含所有颜色的区域
        //        Mat 合并掩码 = new Mat(区域图像.Size(), MatType.CV_8UC1, Scalar.All(255));
        //        List<Rect> 色块区域列表 = 查找包含所有颜色的区域(所有颜色的轮廓列表, 颜色掩码列表, 边缘膨胀大小, 最小面积阈值);

        //        // 寻找距离参考点最近的色块
        //        Rect? 最近色块 = null;

        //        double 最小距离 = double.MaxValue;
        //        foreach (var 色块 in 色块区域列表)
        //        {
        //            Point 色块中心 = new Point(色块.X + 色块.Width / 2, 色块.Y + 色块.Height / 2);
        //            最小距离 = 距离;
        //            最近色块 = 色块;
        //        }

        //        if (最近色块.HasValue)
        //        {
        //            Rect 找到的色块 = 最近色块.Value;
        //            Point 色块中心坐标 = new Point(
        //                找到的色块.X + 找到的色块.Width / 2 + 找色区域.X,
        //                找到的色块.Y + 找到的色块.Height / 2 + 找色区域.Y
        //            );
        //        }


        //        // 释放资源
        //        区域图像.Dispose();
        //        hsv图像.Dispose();
        //        合并掩码.Dispose();
        //    });
        //    颜色集合.Clear();

        //    return default;
        //}
        private static bool 检测中心区域颜色连通(HashSet<Scalar> 颜色集合, Mat 大图, Rect 找色区域, float 色相精度, float 饱和度精度, float 亮度精度)
        {
            // 计算中心40x40区域
            int 中心区域大小 = 40;
            int 中心X = 找色区域.X + 找色区域.Width / 2;
            int 中心Y = 找色区域.Y + 找色区域.Height / 2;

            Rect 中心区域 = new Rect(
                中心X - 中心区域大小 / 2,
                中心Y - 中心区域大小 / 2,
                中心区域大小,
                中心区域大小
            );

            // 确保中心区域在找色区域内
            中心区域.X = Math.Max(找色区域.X, 中心区域.X);
            中心区域.Y = Math.Max(找色区域.Y, 中心区域.Y);
            中心区域.Width = Math.Min(找色区域.Width - (中心区域.X - 找色区域.X), 中心区域大小);
            中心区域.Height = Math.Min(找色区域.Height - (中心区域.Y - 找色区域.Y), 中心区域大小);

            if (中心区域.Width <= 0 || 中心区域.Height <= 0)
                return false;

            using (Mat 区域图像 = new Mat(大图, 中心区域))
            using (Mat hsv图像 = new Mat())
            {
                // 转换为HSV颜色空间
                Cv2.CvtColor(区域图像, hsv图像, ColorConversionCodes.BGR2HSV);

                // 为每种颜色创建掩码
                List<Mat> 颜色掩码列表 = new List<Mat>();
                Dictionary<Scalar, Mat> 颜色到掩码映射 = new Dictionary<Scalar, Mat>();

                foreach (Scalar 目标颜色 in 颜色集合)
                {
                    // 将目标BGR颜色转换为HSV
                    using (Mat 目标颜色Mat = new Mat(1, 1, MatType.CV_8UC3, 目标颜色))
                    using (Mat 目标颜色HSV = new Mat())
                    {
                        Cv2.CvtColor(目标颜色Mat, 目标颜色HSV, ColorConversionCodes.BGR2HSV);
                        Vec3b hsv值 = 目标颜色HSV.At<Vec3b>(0, 0);

                        // 根据精度设置颜色范围
                        int h范围 = (int)(色相精度 * 180);
                        int s范围 = (int)(饱和度精度 * 255);
                        int v范围 = (int)(亮度精度 * 255);

                        Scalar 下限 = new Scalar(
                            Math.Max(0, hsv值.Item0 - h范围),
                            Math.Max(0, hsv值.Item1 - s范围),
                            Math.Max(0, hsv值.Item2 - v范围)
                        );
                        Scalar 上限 = new Scalar(
                            Math.Min(180, hsv值.Item0 + h范围),
                            Math.Min(255, hsv值.Item1 + s范围),
                            Math.Min(255, hsv值.Item2 + v范围)
                        );

                        Mat 颜色掩码 = new Mat();
                        Cv2.InRange(hsv图像, 下限, 上限, 颜色掩码);
                        颜色掩码列表.Add(颜色掩码);
                        颜色到掩码映射[目标颜色] = 颜色掩码;
                    }
                }

                // 检查每种颜色是否至少有一个像素
                bool 所有颜色都存在 = true;
                foreach (var 颜色掩码 in 颜色掩码列表)
                {
                    if (Cv2.CountNonZero(颜色掩码) == 0)
                    {
                        所有颜色都存在 = false;
                        break;
                    }
                }

                if (!所有颜色都存在)
                {
                    // 清理资源
                    foreach (var 掩码 in 颜色掩码列表)
                        掩码.Dispose();
                    return false;
                }

                // 检查颜色是否相连（使用连通组件分析）
                bool 颜色相连 = 检查颜色连通性(颜色掩码列表, 颜色到掩码映射, 中心区域大小);

                // 清理资源
                foreach (var 掩码 in 颜色掩码列表)
                    掩码.Dispose();

                return 颜色相连;
            }
        }

        /// <summary>
        /// 检查所有颜色的像素是否相连（在同一个连通区域内）
        /// </summary>
        private static bool 检查颜色连通性(List<Mat> 颜色掩码列表, Dictionary<Scalar, Mat> 颜色到掩码映射, int 区域大小)
        {
            // 创建合并掩码（所有颜色的并集）
            //using (Mat 合并掩码 = new Mat(颜色掩码列表[0].Size(), MatType.CV_8UC1, Scalar.All(0)))
            //{
            //    foreach (var 颜色掩码 in 颜色掩码列表)
            //    {
            //        Cv2.BitwiseOr(合并掩码, 颜色掩码, 合并掩码);
            //    }

            //    // 查找连通组件
            //    var 连通输出 = Cv2.ConnectedComponentsEx(合并掩码);

            //    if (连通Output.Blobs.Length == 0)
            //        return false;

            //    // 检查每个连通区域是否包含所有颜色
            //    foreach (var blob in 连通Output.Blobs)
            //    {
            //        if (blob.Area < 颜色掩码列表.Count) // 至少要有每个颜色一个像素
            //            continue;

            //        // 提取当前连通区域的掩码
            //        using (Mat 区域掩码 = new Mat(合并掩码.Size(), MatType.CV_8UC1, Scalar.All(0)))
            //        {
            //            // 填充连通区域
            //            Cv2.FloodFill(区域掩码, 合并掩码, blob.Rect.Center, new Scalar(255));

            //            // 检查该区域是否包含所有颜色
            //            bool 包含所有颜色 = true;
            //            foreach (var 颜色掩码 in 颜色掩码列表)
            //            {
            //                using (Mat 交集 = new Mat())
            //                {
            //                    Cv2.BitwiseAnd(区域掩码, 颜色掩码, 交集);
            //                    if (Cv2.CountNonZero(交集) == 0)
            //                    {
            //                        包含所有颜色 = false;
            //                        break;
            //                    }
            //                }
            //            }

            //            if (包含所有颜色)
            //                return true;
            //        }
            //    }

            //    return false;
            //}
            return default;
        }

        /// <summary>
        /// 检查并扩展画布大小
        /// </summary>
        private static OpenCvSharp.Mat 检查并扩展画布(OpenCvSharp.Mat 原图, int 小图宽度, int 小图高度, (int X, int Y, double 匹配度) 匹配位置)
        {
            int 当前宽度 = 原图.Width;
            int 当前高度 = 原图.Height;
            int 安全边界 = Math.Min(小图宽度, 小图高度) / 2;

            // 检查是否需要扩展
            bool 需要左扩展 = 匹配位置.X < 安全边界;
            bool 需要右扩展 = 匹配位置.X + 小图宽度 > 当前宽度 - 安全边界;
            bool 需要上扩展 = 匹配位置.Y < 安全边界;
            bool 需要下扩展 = 匹配位置.Y + 小图高度 > 当前高度 - 安全边界;

            if (!需要左扩展 && !需要右扩展 && !需要上扩展 && !需要下扩展)
                return 原图;

            // 计算新尺寸和偏移
            int 扩展量 = Math.Max(小图宽度, 小图高度);
            int 新宽度 = 当前宽度 + (需要左扩展 ? 扩展量 : 0) + (需要右扩展 ? 扩展量 : 0);
            int 新高度 = 当前高度 + (需要上扩展 ? 扩展量 : 0) + (需要下扩展 ? 扩展量 : 0);
            int 偏移X = 需要左扩展 ? 扩展量 : 0;
            int 偏移Y = 需要上扩展 ? 扩展量 : 0;

            // 创建新画布并复制内容
            OpenCvSharp.Mat 新画布 = new OpenCvSharp.Mat(新高度, 新宽度, OpenCvSharp.MatType.CV_8UC3, new OpenCvSharp.Scalar(255, 255, 255));
            放置帧到位置(新画布, 原图, 偏移X, 偏移Y);
            原图.Dispose();
            return 新画布;
        }
        /// <summary>
        /// 放置帧到指定位置
        /// </summary>
        private static void 放置帧到位置(OpenCvSharp.Mat 完整地图, OpenCvSharp.Mat 帧, int x, int y)
        {
            // 检查边界
            if (x < 0 || y < 0 || x + 帧.Width > 完整地图.Width || y + 帧.Height > 完整地图.Height)
            {
                Console.WriteLine("位置超出边界");
                return;
            }
            // 直接复制到目标区域
            var 目标区域 = new OpenCvSharp.Rect(x, y, 帧.Width, 帧.Height);
            帧.CopyTo(完整地图[目标区域]);
        }
        /// <summary>
        /// 在完整地图中寻找当前帧的最佳匹配位置
        /// </summary>
        private static (int X, int Y, double 匹配度) 图片拼接找图(OpenCvSharp.Mat 完整地图, OpenCvSharp.Mat 当前帧)
        {
            using var 匹配结果 = new OpenCvSharp.Mat();
            Cv2.MatchTemplate(完整地图, 当前帧, 匹配结果, TemplateMatchModes.CCoeffNormed);
            Cv2.MinMaxLoc(匹配结果, out _, out double 最大相似值, out _, out var maxLoc);
            return (maxLoc.X, maxLoc.Y, 最大相似值);
        }
        /// <summary>
        /// 替换背景颜色
        /// </summary>
        private static void 替换背景颜色(OpenCvSharp.Mat 图像, Scalar 原颜色, Scalar 新颜色)
        {
            try
            {
                using var 掩码 = new Mat();
                Cv2.InRange(图像, 原颜色, 原颜色, 掩码);
                图像.SetTo(新颜色, 掩码);
                Console.WriteLine("背景颜色已替换");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"替换背景颜色失败: {ex.Message}");
            }
        }
        /// <summary>
        /// 裁剪掉周围的黑色区域
        /// </summary>
        private static OpenCvSharp.Mat 裁剪黑色区域(OpenCvSharp.Mat 原图)
        {
            try
            {
                // 转换为灰度图
                using var gray = new Mat();
                Cv2.CvtColor(原图, gray, ColorConversionCodes.BGR2GRAY);

                // 二值化，找到非黑色区域
                using var binary = new Mat();
                Cv2.Threshold(gray, binary, 10, 255, ThresholdTypes.Binary);

                // 找到非零像素的边界
                OpenCvSharp.Rect 边界框 = Cv2.BoundingRect(binary);

                Console.WriteLine($"裁剪边界: X={边界框.X}, Y={边界框.Y}, Width={边界框.Width}, Height={边界框.Height}");

                // 裁剪图像
                return new Mat(原图, 边界框);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"裁剪失败: {ex.Message}");
                return 原图.Clone();
            }
        }


        public static void 变化监控(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            int 帧间隔 = 1000 / int.Parse(方法.参数[CS.识图帧数].结果值);
            float 监控时长 = 方法.参数[CS.找图限时][valueType.Float];
            float 变化阈值 = float.Parse(方法.参数[CS.相似度1].结果值);
            int 压缩次数 = int.Parse(方法.参数[CS.图片压缩].值);

            int[] 区域 = 方法.参数[CS.区域].区域s;
            OpenCvSharp.Rect 截图大小 = new OpenCvSharp.Rect(
                区域[0] / 压缩次数.EF(),
                区域[1] / 压缩次数.EF(),
                (区域[2] - 区域[0]) / 压缩次数.EF(),
                (区域[3] - 区域[1]) / 压缩次数.EF()
            );

            Stopwatch 总计时器 = new Stopwatch();
            Stopwatch 帧计时器 = new Stopwatch();
            Mat 前一帧 = null;
            bool 已检测到变化 = false;

            总计时器.Start();

            void 监控循环()
            {
                if (监控时长 > 0 && 总计时器.ElapsedMilliseconds > 监控时长)
                {
                    // 超时未检测到变化
                    方法data.循环num = 0;
                    方法data.信号.ExitTimer();
                    return;
                }

                using (Mat 当前帧 = new Mat(截屏.图片[压缩次数, imageType.Mat], 截图大小))
                {
                    // 转换为灰度图进行比较
                    using (Mat 当前灰度 = new Mat())
                    {
                        Cv2.CvtColor(当前帧, 当前灰度, ColorConversionCodes.BGR2GRAY);

                        if (前一帧 == null)
                        {
                            // 第一帧，保存为参考帧
                            前一帧 = 当前灰度.Clone();
                            return;
                        }

                        // 计算帧间差异
                        using (Mat 差异图 = new Mat())
                        using (Mat 二值图 = new Mat())
                        {
                            Cv2.Absdiff(前一帧, 当前灰度, 差异图);
                            Cv2.Threshold(差异图, 二值图, 30, 255, ThresholdTypes.Binary);

                            // 计算变化像素比例
                            double 非零像素数 = Cv2.CountNonZero(二值图);
                            double 总像素数 = 二值图.Width * 二值图.Height;
                            double 变化比例 = 非零像素数 / 总像素数;

                            if (变化比例 > 变化阈值)
                            {
                                // 检测到显著变化
                                已检测到变化 = true;

                                // 找到变化区域
                                Cv2.FindContours(二值图, out OpenCvSharp.Point[][] 轮廓,
                                                out HierarchyIndex[] hierarchy,
                                                RetrievalModes.External,
                                                ContourApproximationModes.ApproxSimple);

                                if (轮廓.Length > 0)
                                {
                                    // 计算所有轮廓的外接矩形
                                    var 变化区域 = new List<OpenCvSharp.Rect>();
                                    foreach (var contour in 轮廓)
                                    {
                                        if (Cv2.ContourArea(contour) > 10) // 过滤小噪点
                                        {
                                            var rect = Cv2.BoundingRect(contour);
                                            // 转换回原始坐标
                                            rect = new OpenCvSharp.Rect(
                                                区域[0] + rect.X * 压缩次数.EF(),
                                                区域[1] + rect.Y * 压缩次数.EF(),
                                                rect.Width * 压缩次数.EF(),
                                                rect.Height * 压缩次数.EF()
                                            );
                                            变化区域.Add(rect);
                                        }
                                    }

                                    // 绘制变化区域（可选）
                                    if (数据库.参数[CS.框选目标].结果值 == "1" && 变化区域.Count > 0)
                                    {
                                        前景窗口.绘制(变化区域, Enumerable.Repeat(-1, 变化区域.Count).ToList());
                                    }

                                    // 设置返回参数
                                    if (变化区域.Count > 0)
                                    {
                                        var 主区域 = 变化区域[0]; // 取第一个变化区域
                                        方法.参数[CS.目标区域].值.赋值($"{主区域.X},{主区域.Y},{主区域.Width},{主区域.Height}");
                                        方法.参数[CS.坐标].值.赋值($"{主区域.X + 主区域.Width / 2},{主区域.Y + 主区域.Height / 2}");
                                    }
                                }

                                方法data.循环num = 1;
                                方法data.信号.ExitTimer();
                                return;
                            }

                            // 更新前一帧（可选：可以设置更新间隔来避免微小变化累积）
                            帧计时器.Restart();
                            if (帧计时器.ElapsedMilliseconds > 100) // 每100ms更新一次参考帧
                            {
                                前一帧?.Dispose();
                                前一帧 = 当前灰度.Clone();
                                帧计时器.Restart();
                            }
                        }
                    }
                }
            }

            方法data.信号.Timer(监控循环, 帧间隔);
            前一帧?.Dispose();
            总计时器.Stop();
            帧计时器.Stop();
        }
        public static void 模板找图(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            bool 取反 = 方法.参数[CS.布尔值].结果值 == "1";
            bool 找所有图 = 方法.参数[CS.找所有图].结果值 == "1";
            int 图X = -1, 图Y = -1;
            float 相似度 = float.Parse(方法.参数[CS.相似度1].结果值);
            float H相似度 = float.Parse(方法.参数[CS.H].结果值);
            float S相似度 = float.Parse(方法.参数[CS.S].结果值);
            float V相似度 = float.Parse(方法.参数[CS.V].结果值);

            // 判断是否使用灰度图
            bool 使用灰度图 = H相似度 == 0 && S相似度 == 0 && V相似度 == 0;

            ParallelOptions 最大线程 = new();
            最大线程.MaxDegreeOfParallelism = int.Parse(方法.参数[CS.识图线程数量].值);
            int 压缩次数 = int.Parse(方法.参数[CS.图片压缩].值);

            ConcurrentBag<(OpenCvSharp.Rect, int, double)> 所有目标 = new();

            List<MoyuBitmap> bitmaps = 方法.参数[CS.图片1].值.取图();
            List<Mat> 小图s = new();
            int ID计数;
            for (int i = 0; i < bitmaps.Count; i++)
            {
                Mat 原图 = bitmaps[i].Mat;
                for (int j = 0; j < 压缩次数; j++)
                    原图 = 原图.PyrDown();

                Mat 处理图 = new();
                if (使用灰度图)
                {
                    // 使用灰度图
                    Cv2.CvtColor(原图, 处理图, ColorConversionCodes.BGR2GRAY);
                }
                else
                {
                    // 使用HSV图
                    Cv2.CvtColor(原图, 处理图, ColorConversionCodes.BGR2HSV);
                    Cv2.Split(处理图, out Mat[] 小图HS);
                    Cv2.Merge(小图HS, 处理图);
                    小图HS = null;
                }
                原图 = null;
                小图s.Add(处理图);
            }
            bitmaps = null;

            OpenCvSharp.Rect 截图大小;
            int[] 区域 = 方法.参数[CS.区域].区域s;
            截图大小 = new OpenCvSharp.Rect(区域[0] / 压缩次数.EF(), 区域[1] / 压缩次数.EF(), (区域[2] - 区域[0]) / 压缩次数.EF(), (区域[3] - 区域[1]) / 压缩次数.EF());

            // 解析坐标2参数（用于最近距离计算）
            int[] 参考坐标 = 方法.参数[CS.坐标2].结果值.Split(',').Select(int.Parse).ToArray();
            IEnumerable<Mat> 分区集 = Partitioner.Create(小图s, true).GetDynamicPartitions();

            Using(new Mat(截屏.图片[压缩次数, imageType.Mat], 截图大小), 大图mat =>
            {
                ID计数 = -1;

                if (使用灰度图)
                {
                    // 转换为灰度图
                    Cv2.CvtColor(大图mat, 大图mat, ColorConversionCodes.BGR2GRAY);
                }
                else
                {
                    // 转换为HSV图
                    Cv2.CvtColor(大图mat, 大图mat, ColorConversionCodes.BGR2HSV);
                }

                // 分离HSV通道用于单独相似度判断（仅在非灰度模式下）
                Mat[] 大图HSV = null;
                if (!使用灰度图)
                {
                    Cv2.Split(大图mat, out 大图HSV);
                }

                Parallel.ForEach(分区集, 最大线程, 小图 =>
                {
                    int 小图ID = Interlocked.Increment(ref ID计数);
                    Using(new Mat(), 矩阵 =>
                    {
                        // 主模板匹配
                        Cv2.MatchTemplate(大图mat, 小图, 矩阵, TemplateMatchModes.CCoeffNormed);

                        Cv2.MinMaxLoc(矩阵, out double 最小相似值, out double 最大相似值, out OpenCvSharp.Point 最小相似点, out OpenCvSharp.Point 最大相似点);
                        Cv2.Threshold(矩阵, 矩阵, 相似度, 1.0, ThresholdTypes.Binary);
                        OpenCvSharp.Point[][] 轮廓;
                        HierarchyIndex[] hierarchy;
                        矩阵.ConvertTo(矩阵, MatType.CV_8UC1);
                        Cv2.FindContours(矩阵, out 轮廓, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxTC89KCOS);

                        if (轮廓.Length > 0)
                        {
                            foreach (var contour in 轮廓)
                            {
                                OpenCvSharp.Rect rect = Cv2.BoundingRect(contour);

                                // HSV通道单独验证（仅在非灰度模式下且设置了HSV相似度）
                                bool HSV验证通过 = true;
                                if (!使用灰度图 && (H相似度 < 1.0 || S相似度 < 1.0 || V相似度 < 1.0))
                                {
                                    HSV验证通过 = 验证HSV相似度(大图HSV, 小图, rect, H相似度, S相似度, V相似度);
                                }

                                if (HSV验证通过)
                                {
                                    rect = new OpenCvSharp.Rect(区域[0] + rect.X * 压缩次数.EF(), 区域[1] + rect.Y * 压缩次数.EF(), 小图.Width * 压缩次数.EF(), 小图.Height * 压缩次数.EF());
                                    所有目标.Add((rect, 小图ID, 最大相似值));
                                }
                            }
                        }
                    });
                });

                // 释放HSV通道矩阵（如果存在）
                if (大图HSV != null)
                {
                    foreach (var channel in 大图HSV)
                    {
                        channel?.Dispose();
                    }
                }
            });

            // 清理小图资源
            foreach (var 小图 in 小图s)
            {
                小图?.Dispose();
            }
            bool 找到 = false;
            if (所有目标.Count > 0)
            {
                if (找所有图)
                {
                    // 找所有图模式：必须找到所有图片，然后选择距离参考坐标最近的一个
                    var 目标列表 = 所有目标.ToList();

                    // 按图片ID分组，确保每个小图都至少找到一个匹配
                    var 分组目标 = 目标列表.GroupBy(t => t.Item2).ToList();

                    // 检查是否所有小图都找到了匹配
                    bool 所有图片都找到 = 分组目标.Count == 小图s.Count;

                    if (所有图片都找到)
                    {
                        // 所有图片都找到了匹配，现在从所有目标中选择距离参考坐标最近的一个
                        OpenCvSharp.Rect 最近矩形 = 寻找最近目标(所有目标, 参考坐标[0], 参考坐标[1]);

                        // 绘制所有找到的目标
                        if (数据库.参数[CS.框选目标].结果值 == "1")
                        {
                            var 所有矩形 = 目标列表.Select(t => t.Item1).ToList();
                            前景窗口.绘制(所有矩形, Enumerable.Repeat(-1, 所有矩形.Count).ToList());
                        }

                        // 设置返回参数为最近的目标
                        OpenCvSharp.Rect 最相似矩形 = new(最近矩形.X, 最近矩形.Y, 最近矩形.Width, 最近矩形.Height);
                        图X = 最相似矩形.X + 最相似矩形.Width / 2;
                        图Y = 最相似矩形.Y + 最相似矩形.Height / 2;
                        方法.参数[CS.目标区域].值.赋值($"{最相似矩形.X},{最相似矩形.Y},{最相似矩形.Width},{最相似矩形.Height}");
                        方法.参数[CS.坐标].值.赋值($"{图X},{图Y}");
                        找到 = true;
                    }
                }
                else
                {
                    // 找单个图模式
                    OpenCvSharp.Rect 目标矩形;
                    // 寻找距离参考坐标最近的图
                    目标矩形 = 寻找最近目标(所有目标, 参考坐标[0], 参考坐标[1]);

                    if (数据库.参数[CS.框选目标].值 == "1")
                        前景窗口.绘制(new() { 目标矩形 }, new() { -1 });

                    OpenCvSharp.Rect 最相似矩形 = new(目标矩形.X, 目标矩形.Y, 目标矩形.Width, 目标矩形.Height);
                    图X = 最相似矩形.X + 最相似矩形.Width / 2;
                    图Y = 最相似矩形.Y + 最相似矩形.Height / 2;
                    方法.参数[CS.目标区域].值.赋值($"{最相似矩形.X},{最相似矩形.Y},{最相似矩形.Width},{最相似矩形.Height}");
                    方法.参数[CS.坐标].值.赋值($"{图X},{图Y}");
                    找到 = true;
                }
            }

            if (找到 && !取反)
                方法data.循环num = 1;
            else if (取反 && !找到)
                方法data.循环num = 1;
            else
                方法data.循环num = 0;
        }
        // HSV通道相似度验证辅助方法
        private static bool 验证HSV相似度(Mat[] 大图HSV, Mat 小图, OpenCvSharp.Rect rect, float H相似度, float S相似度, float V相似度)
        {
            using (Mat 小图HSV = new Mat())
            {
                Cv2.CvtColor(小图, 小图HSV, ColorConversionCodes.BGR2HSV);
                Cv2.Split(小图HSV, out Mat[] 小图HSV通道);

                bool 通过 = true;

                // 对每个通道进行相似度验证
                for (int i = 0; i < 3; i++)
                {
                    using (Mat 大图通道ROI = new Mat(大图HSV[i], rect))
                    using (Mat 小图通道 = 小图HSV通道[i])
                    using (Mat 结果矩阵 = new Mat())
                    {
                        float 当前相似度要求 = i == 0 ? H相似度 : i == 1 ? S相似度 : V相似度;

                        Cv2.MatchTemplate(大图通道ROI, 小图通道, 结果矩阵, TemplateMatchModes.CCoeffNormed);
                        Cv2.MinMaxLoc(结果矩阵, out _, out double 最大相似值, out _, out _);

                        if (最大相似值 < 当前相似度要求)
                        {
                            通过 = false;
                            break;
                        }
                    }
                }

                // 释放小图HSV通道
                foreach (var channel in 小图HSV通道)
                {
                    channel?.Dispose();
                }

                return 通过;
            }
        }
        // 寻找最近目标辅助方法
        private static OpenCvSharp.Rect 寻找最近目标(ConcurrentBag<(OpenCvSharp.Rect, int, double)> 所有目标, int 参考X, int 参考Y)
        {
            var 目标列表 = 所有目标.ToList();
            if (目标列表.Count == 0) return new OpenCvSharp.Rect();

            double 最小距离 = double.MaxValue;
            OpenCvSharp.Rect 最近矩形 = 目标列表[0].Item1;

            foreach (var (rect, _, _) in 目标列表)
            {
                int 中心X = rect.X + rect.Width / 2;
                int 中心Y = rect.Y + rect.Height / 2;
                double 距离 = Math.Sqrt(Math.Pow(中心X - 参考X, 2) + Math.Pow(中心Y - 参考Y, 2));

                if (距离 < 最小距离)
                {
                    最小距离 = 距离;
                    最近矩形 = rect;
                }
            }

            return 最近矩形;
        }

        // 定义外包矩形结构
        public struct BoundingRect
        {
            public int X;
            public int Y;
            public int Width;
            public int Height;
            public Point Center;

            public override string ToString()
            {
                return $"位置: ({X}, {Y}), 大小: {Width}x{Height}";
            }

            public Rect ToRect()
            {
                return new Rect(X, Y, Width, Height);
            }
        }

        public static void 特征匹配(核心._数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            float 相似度 = 1 - float.Parse(方法.参数[CS.相似度1].结果值);
            int 忽略孤立点数量 = 方法.参数[CS.数字1][valueType.Int];
            bool 找所有图 = 方法.参数[CS.找所有图][valueType.Bool];
            bool 取反 = 方法.参数[CS.布尔值][valueType.Bool];
            Rect 找图区域 = 方法.参数[CS.区域].区域opencvRect;
            Point 参考点 = 方法.参数[CS.坐标2].opencv坐标;
            int 图片压缩倍数 = 方法.参数[CS.图片压缩][valueType.Int];
            string 检测器类型 = 方法.参数[CS.特征检测器].结果值;

            // 加载所有小图
            var 所有小图 = 方法.参数[CS.图片1].值.取图();
            using var 大图原始 = 截屏.图片[0, imageType.Mat];

            // 应用图片压缩
            using var 大图 = new Mat();
            if (图片压缩倍数 > 1)
            {
                Cv2.Resize(大图原始, 大图, new Size(大图原始.Width / 图片压缩倍数, 大图原始.Height / 图片压缩倍数));
            }
            else
            {
                大图原始.CopyTo(大图);
            }

            // 限制找图区域
            if (找图区域.Width > 0 && 找图区域.Height > 0 &&
                找图区域.X < 大图.Width && 找图区域.Y < 大图.Height)
            {
                using var 区域大图 = new Mat(大图, 找图区域);
                区域大图.CopyTo(大图);
            }

            // 存储每个小图找到的目标区域
            List<List<BoundingRect>> 每个小图的目标列表 = new List<List<BoundingRect>>(所有小图.Count);

            // 创建特征检测器（一次性创建）
            using var 特征检测器 = 创建特征检测器(检测器类型);

            // 预分配内存
            const int borderSize = 100;
            var blackScalar = Scalar.Black;
            var borderType = BorderTypes.Constant;

            // 并行处理所有小图（如果小图数量多）
            if (所有小图.Count > 1)
            {
                var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
                var 并行目标列表 = new List<BoundingRect>[所有小图.Count];

                Parallel.For(0, 所有小图.Count, parallelOptions, i =>
                {
                    var 小图数据 = 所有小图[i];
                    并行目标列表[i] = 处理单张小图(小图数据.Mat, 大图, 特征检测器, 相似度, 忽略孤立点数量,
                        找图区域, 图片压缩倍数, borderSize, blackScalar, borderType);
                });

                每个小图的目标列表.AddRange(并行目标列表);
            }
            else
            {
                // 单张小图顺序处理
                foreach (var 小图数据 in 所有小图)
                {
                    var 目标列表 = 处理单张小图(小图数据.Mat, 大图, 特征检测器, 相似度, 忽略孤立点数量,
                        找图区域, 图片压缩倍数, borderSize, blackScalar, borderType);
                    每个小图的目标列表.Add(目标列表);
                }
            }

            // 判断是否所有小图都找到了至少一个有效特征区域
            bool 所有小图都找到目标 = 每个小图的目标列表.All(目标列表 => 目标列表.Count > 0);

            // 收集所有找到的目标
            List<BoundingRect> 所有找到的目标 = 每个小图的目标列表
                .SelectMany(目标列表 => 目标列表)
                .ToList();

            // 根据找所有图的设置选择目标
            BoundingRect? 最终目标 = null;
            bool 找到目标 = 所有找到的目标.Count > 0;

            if (找到目标)
            {
                if (找所有图)
                {
                    // 找所有图时，需要所有小图都至少找到一个目标
                    if (所有小图都找到目标)
                    {
                        // 选择距离参考点最近的目标
                        最终目标 = 所有找到的目标
                            .OrderBy(rect => Distance(rect.Center, 参考点))
                            .First();
                    }
                    else
                    {
                        找到目标 = false;
                    }
                }
                else
                {
                    // 不找所有图时，选择距离参考点最近的任意目标
                    最终目标 = 所有找到的目标
                        .OrderBy(rect => Distance(rect.Center, 参考点))
                        .First();
                }
            }

            // 设置结果
            if (最终目标 != null && 找到目标 && !取反)
            {
                方法.参数[CS.坐标].值.赋值($"{最终目标.Value.X},{最终目标.Value.Y}");
                方法.参数[CS.目标区域].值.赋值($"{最终目标.Value.X},{最终目标.Value.Y},{最终目标.Value.Width},{最终目标.Value.Height}");
                方法data.循环num = 1;
            }
            else if (取反 && !找到目标)
            {
                方法data.循环num = 1;
            }
        }
        // 处理单张小图的匹配
        private static List<BoundingRect> 处理单张小图(Mat 小图, Mat 大图, Feature2D 特征检测器,
            float 相似度, int 忽略孤立点数量, Rect 找图区域, int 图片压缩倍数,
            int borderSize, Scalar borderColor, BorderTypes borderType)
        {
            var 当前小图目标 = new List<BoundingRect>();

            using var 小图带边框 = new Mat();
            using var 大图带边框 = new Mat();

            // 添加边框
            Cv2.CopyMakeBorder(小图, 小图带边框, borderSize, borderSize, borderSize, borderSize, borderType, borderColor);
            Cv2.CopyMakeBorder(大图, 大图带边框, borderSize, borderSize, borderSize, borderSize, borderType, borderColor);

            // 检测特征点
            KeyPoint[] 小图KeyPoints, 大图KeyPoints;
            using var 小图Descriptors = new Mat();
            using var 大图Descriptors = new Mat();

            特征检测器.DetectAndCompute(小图带边框, null, out 小图KeyPoints, 小图Descriptors);
            特征检测器.DetectAndCompute(大图带边框, null, out 大图KeyPoints, 大图Descriptors);

            // 如果特征点太少，直接返回
            if (小图KeyPoints.Length < 3 || 大图KeyPoints.Length < 3)
                return 当前小图目标;

            // 根据描述符类型选择合适的匹配器
            using var matcher = 创建匹配器(小图Descriptors.Type());

            try
            {
                // 特征匹配
                var knnMatches = matcher.KnnMatch(大图Descriptors, 小图Descriptors, 2);

                // 使用Lowe's ratio test筛选匹配点
                var goodMatches = new List<DMatch>(knnMatches.Length / 2);
                foreach (var matchPair in knnMatches)
                {
                    if (matchPair.Length >= 2 && matchPair[0].Distance < 相似度 * matchPair[1].Distance)
                    {
                        goodMatches.Add(matchPair[0]);
                    }
                }

                // 处理匹配结果
                if (goodMatches.Count > 2)
                {
                    // 预分配点集内存
                    var 小图点集 = new List<Point2f>(goodMatches.Count);
                    var 大图点集 = new List<Point2f>(goodMatches.Count);

                    foreach (var match in goodMatches)
                    {
                        小图点集.Add(小图KeyPoints[match.TrainIdx].Pt);
                        大图点集.Add(大图KeyPoints[match.QueryIdx].Pt);
                    }

                    // 聚类分析
                    var clusters = DBSCANClustering(大图点集, 60, 3);

                    foreach (var cluster in clusters)
                    {
                        if (cluster.Count > 忽略孤立点数量)
                        {
                            var boundingRect = CalculateBoundingRect(cluster, 大图点集);

                            // 调整坐标
                            boundingRect.X = boundingRect.X - borderSize + 找图区域.X;
                            boundingRect.Y = boundingRect.Y - borderSize + 找图区域.Y;

                            // 应用图片压缩倍数
                            if (图片压缩倍数 > 1)
                            {
                                boundingRect.X *= 图片压缩倍数;
                                boundingRect.Y *= 图片压缩倍数;
                                boundingRect.Width *= 图片压缩倍数;
                                boundingRect.Height *= 图片压缩倍数;
                            }

                            // 更新中心点
                            boundingRect.Center = new Point(
                                boundingRect.X + boundingRect.Width / 2,
                                boundingRect.Y + boundingRect.Height / 2
                            );

                            当前小图目标.Add(boundingRect);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // 记录错误但继续处理其他图片
                System.Diagnostics.Debug.WriteLine($"特征匹配错误: {ex.Message}");
                return 当前小图目标;
            }

            return 当前小图目标;
        }
        // 根据描述符类型创建合适的匹配器
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static DescriptorMatcher 创建匹配器(MatType 描述符类型)
        {
            // 判断描述符类型，选择不同的匹配器
            if (描述符类型 == MatType.CV_8U || 描述符类型 == MatType.CV_8UC1)
            {
                // 二进制描述符（ORB, BRISK等）使用汉明距离匹配器
                return new BFMatcher(NormTypes.Hamming);
            }
            else
            {
                // 浮点描述符（SIFT, KAZE, AKAZE等）使用L2距离匹配器
                return new FlannBasedMatcher();
            }
        }
        // 创建特征检测器 - 添加参数配置
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static Feature2D 创建特征检测器(string 检测器类型)
        {
            return 检测器类型.ToUpperInvariant() switch
            {
                "ORB" => ORB.Create(
                    nFeatures: 500,
                    scaleFactor: 1.2f,
                    nLevels: 8,
                    edgeThreshold: 31,
                    firstLevel: 0,
                    wtaK: 2,
                    scoreType: ORBScoreType.Harris,
                    patchSize: 31,
                    fastThreshold: 20),
                "BRISK" => BRISK.Create(
                    thresh: 30,
                    octaves: 3,
                    patternScale: 1.0f),
                _ => SIFT.Create( // 默认使用SIFT
                    nFeatures: 0,
                    nOctaveLayers: 3,
                    contrastThreshold: 0.04,
                    edgeThreshold: 10,
                    sigma: 1.6)
            };
        }
        // 优化后的距离计算方法
        [SkipLocalsInit]
        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static double Distance(Point p1, Point p2)
        {
            double dx = p1.X - p2.X;
            double dy = p1.Y - p2.Y;
            return Math.Sqrt(dx * dx + dy * dy);
        }
        // 优化后的邻居查找
        private static List<int> GetNeighbors(List<Point2f> points, int index, double eps)
        {
            var neighbors = new List<int>();
            var point = points[index];
            double epsSquared = eps * eps; // 使用平方避免开方运算

            for (int i = 0; i < points.Count; i++)
            {
                if (i == index) continue;

                var other = points[i];
                double dx = point.X - other.X;
                double dy = point.Y - other.Y;
                double distanceSquared = dx * dx + dy * dy;

                if (distanceSquared <= epsSquared)
                {
                    neighbors.Add(i);
                }
            }

            return neighbors;
        }

        // 计算聚类的外包矩形（保持不变）
        private static BoundingRect CalculateBoundingRect(List<int> cluster, List<Point2f> points)
        {
            if (cluster.Count == 0) return new BoundingRect();

            float minX = float.MaxValue, minY = float.MaxValue;
            float maxX = float.MinValue, maxY = float.MinValue;

            foreach (int index in cluster)
            {
                var point = points[index];
                minX = Math.Min(minX, point.X);
                minY = Math.Min(minY, point.Y);
                maxX = Math.Max(maxX, point.X);
                maxY = Math.Max(maxY, point.Y);
            }

            return new BoundingRect
            {
                X = (int)minX,
                Y = (int)minY,
                Width = (int)(maxX - minX),
                Height = (int)(maxY - minY),
                Center = new Point((int)((minX + maxX) / 2), (int)((minY + maxY) / 2))
            };
        }

        // DBSCAN聚类（保持不变）
        private static List<List<int>> DBSCANClustering(List<Point2f> points, double eps, int minPts)
        {
            int n = points.Count;
            bool[] visited = new bool[n];
            List<List<int>> clusters = new List<List<int>>();

            for (int i = 0; i < n; i++)
            {
                if (visited[i]) continue;

                visited[i] = true;
                List<int> neighbors = GetNeighbors(points, i, eps);

                if (neighbors.Count < minPts)
                {
                    continue;
                }

                List<int> cluster = new List<int> { i };
                clusters.Add(cluster);

                for (int j = 0; j < neighbors.Count; j++)
                {
                    int neighborIdx = neighbors[j];

                    if (!visited[neighborIdx])
                    {
                        visited[neighborIdx] = true;
                        List<int> neighborNeighbors = GetNeighbors(points, neighborIdx, eps);

                        if (neighborNeighbors.Count >= minPts)
                        {
                            neighbors.AddRange(neighborNeighbors);
                        }
                    }

                    if (!cluster.Contains(neighborIdx))
                    {
                        cluster.Add(neighborIdx);
                    }
                }
            }

            return clusters;
        }
        public static void 找色块(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            List<MoyuBitmap> bitmaps = 方法.参数[CS.图片1].值.取图();

            // 从色块图中提取所有颜色
            float 最小面积阈值 = float.MinValue;
            float 面积精度 = 方法.参数[CS.面积精度][valueType.Float];
            HashSet<Scalar> 颜色集合 = new HashSet<Scalar>();
            foreach (var bitmap in bitmaps)
            {
                var mat = bitmap.Mat;
                int width = mat.Width;
                int height = mat.Height;
                float 面积 = width * height * 面积精度;
                if (面积 > 最小面积阈值)
                    最小面积阈值 = 面积;
                // 遍历图片中的所有像素来收集颜色
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                        颜色集合.Add(mat.GetScalar(x, y));
                }
            }

            OpenCvSharp.Point 参考坐标 = 方法.参数[CS.坐标2].opencv坐标;
            Rect 找色区域 = 方法.参数[CS.区域].区域opencvRect;
            float 色相精度 = 1 - 方法.参数[CS.H][valueType.Float];
            float 饱和度精度 = 1 - 方法.参数[CS.S][valueType.Float];
            float 亮度精度 = 1 - 方法.参数[CS.V][valueType.Float];
            int 图片压缩倍数 = 方法.参数[CS.图片压缩][valueType.Int];
            bool 取反 = 方法.参数[CS.布尔值][valueType.Bool];
            int 边缘膨胀大小 = 方法.参数[CS.数字1][valueType.Int];

            Using(截屏.图片[0, imageType.Mat], 大图 =>
            {
                // 应用图片压缩
                if (图片压缩倍数 > 1)
                {
                    大图 = 大图.Resize(new Size(大图.Width / 图片压缩倍数, 大图.Height / 图片压缩倍数));
                    找色区域 = new Rect(找色区域.X / 图片压缩倍数, 找色区域.Y / 图片压缩倍数,
                                     找色区域.Width / 图片压缩倍数, 找色区域.Height / 图片压缩倍数);
                }

                // 提取找色区域
                Mat 区域图像 = new Mat(大图, 找色区域);

                // 转换为HSV颜色空间
                Mat hsv图像 = new Mat();
                Cv2.CvtColor(区域图像, hsv图像, ColorConversionCodes.BGR2HSV);

                // 存储每个颜色的掩码
                List<Mat> 颜色掩码列表 = new List<Mat>();
                foreach (Scalar 目标颜色 in 颜色集合)
                {
                    // 将目标BGR颜色转换为HSV
                    Mat 目标颜色Mat = new Mat(1, 1, MatType.CV_8UC3, 目标颜色);
                    Mat 目标颜色HSV = new Mat();
                    Cv2.CvtColor(目标颜色Mat, 目标颜色HSV, ColorConversionCodes.BGR2HSV);
                    Vec3b hsv值 = 目标颜色HSV.At<Vec3b>(0, 0);

                    // 根据精度设置颜色范围
                    int h范围 = (int)(色相精度 * 180);
                    int s范围 = (int)(饱和度精度 * 255);
                    int v范围 = (int)(亮度精度 * 255);

                    Scalar 下限 = new Scalar(
                        Math.Max(0, hsv值.Item0 - h范围),
                        Math.Max(0, hsv值.Item1 - s范围),
                        Math.Max(0, hsv值.Item2 - v范围)
                    );
                    Scalar 上限 = new Scalar(
                        Math.Min(180, hsv值.Item0 + h范围),
                        Math.Min(255, hsv值.Item1 + s范围),
                        Math.Min(255, hsv值.Item2 + v范围)
                    );

                    Mat 颜色掩码 = new Mat();
                    Cv2.InRange(hsv图像, 下限, 上限, 颜色掩码);

                    颜色掩码列表.Add(颜色掩码);
                }

                // 使用轮廓交集
                List<List<Rect>> 所有颜色的轮廓列表 = new List<List<Rect>>();
                foreach (var 颜色掩码 in 颜色掩码列表)
                {
                    Point[][] 轮廓数组;
                    HierarchyIndex[] 层级数组;
                    Cv2.FindContours(颜色掩码, out 轮廓数组, out 层级数组, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

                    List<Rect> 当前颜色轮廓 = new List<Rect>();
                    foreach (var 轮廓 in 轮廓数组)
                    {
                        double 轮廓面积 = Cv2.ContourArea(轮廓);
                        Rect 边界矩形 = Cv2.BoundingRect(轮廓);
                        当前颜色轮廓.Add(边界矩形);
                    }
                    所有颜色的轮廓列表.Add(当前颜色轮廓);
                }

                // 找到包含所有颜色的区域
                Mat 合并掩码 = new Mat(区域图像.Size(), MatType.CV_8UC1, Scalar.All(255));
                List<Rect> 色块区域列表 = 查找包含所有颜色的区域(所有颜色的轮廓列表, 颜色掩码列表, 边缘膨胀大小, 最小面积阈值);

                // 寻找距离参考点最近的色块
                Rect? 最近色块 = null;
                double 最小距离 = double.MaxValue;

                foreach (var 色块 in 色块区域列表)
                {
                    Point 色块中心 = new Point(色块.X + 色块.Width / 2, 色块.Y + 色块.Height / 2);

                    // 计算到参考点的距离
                    double 距离 = Math.Sqrt(Math.Pow(色块中心.X - 参考坐标.X, 2) +
                                       Math.Pow(色块中心.Y - 参考坐标.Y, 2));

                    if (距离 < 最小距离)
                    {
                        最小距离 = 距离;
                        最近色块 = 色块;
                    }
                }

                bool 找到 = false;
                if (最近色块.HasValue)
                {
                    Rect 找到的色块 = 最近色块.Value;
                    Point 色块中心坐标 = new Point(
                        找到的色块.X + 找到的色块.Width / 2 + 找色区域.X,
                        找到的色块.Y + 找到的色块.Height / 2 + 找色区域.Y
                    );

                    // 如果压缩过，需要还原坐标
                    if (图片压缩倍数 > 1)
                    {
                        色块中心坐标 = new Point(色块中心坐标.X * 图片压缩倍数, 色块中心坐标.Y * 图片压缩倍数);
                        找到的色块 = new Rect(
                            找到的色块.X * 图片压缩倍数,
                            找到的色块.Y * 图片压缩倍数,
                            找到的色块.Width * 图片压缩倍数,
                            找到的色块.Height * 图片压缩倍数
                        );
                    }

                    // 设置输出结果
                    方法.参数[CS.坐标].值.赋值($"{色块中心坐标.X},{色块中心坐标.Y}");
                    方法.参数[CS.目标区域].值.赋值($"{找到的色块.X},{找到的色块.Y},{找到的色块.Width},{找到的色块.Height}");
                    找到 = true;
                }

                if (取反 && !找到)
                    方法data.循环num = 1;
                else if (找到 && !取反)
                    方法data.循环num = 1;
                else
                    方法data.循环num = 0;

                // 释放资源
                区域图像.Dispose();
                hsv图像.Dispose();
                合并掩码.Dispose();
            });
            颜色集合.Clear();
        }
        private static List<Rect> 查找包含所有颜色的区域(List<List<Rect>> 所有颜色的轮廓列表, List<Mat> 颜色掩码列表, int 膨胀大小, float 最小面积阈值)
        {
            List<Rect> 结果 = new List<Rect>();

            if (所有颜色的轮廓列表.Count == 0) return 结果;
            if (所有颜色的轮廓列表.Count == 1) return 所有颜色的轮廓列表[0];

            // 步骤1：合并所有颜色掩码找到候选区域
            Mat 合并掩码 = new Mat(颜色掩码列表[0].Size(), MatType.CV_8UC1, Scalar.All(0));
            foreach (var 颜色掩码 in 颜色掩码列表)
            {
                Cv2.BitwiseOr(合并掩码, 颜色掩码, 合并掩码);
            }

            // 膨胀连接相近区域
            if (膨胀大小 > 0)
            {
                Mat 膨胀核 = Cv2.GetStructuringElement(MorphShapes.Ellipse,
                    new Size(膨胀大小 * 2 + 1, 膨胀大小 * 2 + 1));
                Cv2.MorphologyEx(合并掩码, 合并掩码, MorphTypes.Close, 膨胀核);
            }

            // 找到所有候选轮廓
            Point[][] 候选轮廓数组;
            HierarchyIndex[] 候选层级数组;
            Cv2.FindContours(合并掩码, out 候选轮廓数组, out 候选层级数组, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

            // 步骤2：验证每个候选轮廓是否包含所有颜色
            foreach (var 轮廓 in 候选轮廓数组)
            {
                double 轮廓面积 = Cv2.ContourArea(轮廓);
                if (轮廓面积 >= 最小面积阈值)
                {
                    Rect 边界矩形 = Cv2.BoundingRect(轮廓);

                    // 验证该区域是否包含所有颜色
                    if (区域包含所有颜色(边界矩形, 颜色掩码列表))
                    {
                        结果.Add(边界矩形);
                    }
                }
            }

            合并掩码.Dispose();
            return 结果;
        }
        private static bool 区域包含所有颜色(Rect 区域, List<Mat> 颜色掩码列表)
        {
            // 为每个颜色创建该区域的子掩码
            foreach (var 颜色掩码 in 颜色掩码列表)
            {
                Mat 区域掩码 = new Mat(颜色掩码, 区域);

                // 检查该区域内是否有当前颜色
                double 非零像素数 = Cv2.CountNonZero(区域掩码);
                区域掩码.Dispose();

                // 如果某个颜色在该区域内完全不存在，返回false
                if (非零像素数 == 0)
                    return false;
            }

            return true;
        }
        private static Rect 膨胀矩形(Rect rect, int 像素)
        {
            return new Rect(
                rect.X - 像素,
                rect.Y - 像素,
                rect.Width + 像素 * 2,
                rect.Height + 像素 * 2
            );
        }


        [SkipLocalsInit]
        public unsafe struct 色块
        {
            public 色块() { }
            public float[] 距离 = new float[1];
            public List<double> 面积 = new();
            public List<float> Left = new();
            public List<float> Top = new();
            public List<Rect> Rect = new();
            public float[] X = null;
            public float[] Y = null;
            public List<float> Width = new();
            public List<float> Height = new();
            public List<Scalar> d色 = new();
            public List<Scalar> u色 = new();
            public double d面积 = default;
            public double u面积 = default;

            public void 初始化()
            {
                距离 = new float[1];
                面积.Clear();
                Left.Clear();
                Top.Clear();
                Rect.Clear();
                X = null;
                Y = null;
                Width.Clear();
                Height.Clear();
            }
        }
        [SkipLocalsInit]
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static unsafe Scalar Scalar计算(颜色 color, bool 减法, float 标量)
        {
            if (减法)
            {
                double R = color.R < 标量 ? 0 : color.R - 标量;
                double G = color.G < 标量 ? 0 : color.G - 标量;
                double B = color.B < 标量 ? 0 : color.B - 标量;
                return new(B, G, R, 0);
            }
            else
            {
                byte 差值 = (byte)(255 - 标量);
                double R = color.R > 差值 ? 255 : color.R + 标量;
                double G = color.G > 差值 ? 255 : color.G + 标量;
                double B = color.B > 差值 ? 255 : color.B + 标量;
                return new(B, G, R, 255);
            }
        }

        public static void 找单色(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            List<MoyuBitmap> bitmaps = 方法.参数[CS.图片1].值.取图();

            // 提取所有目标颜色
            List<Scalar> 颜色列表 = new();
            for (int i = 0; i < bitmaps.Count; i++)
                颜色列表.Add(bitmaps[i].Mat.GetScalar(0, 0));

            Rect 找色区域 = 方法.参数[CS.区域].区域opencvRect;
            float 色相精度 = 1 - 方法.参数[CS.H][valueType.Float];
            float 饱和度精度 = 1 - 方法.参数[CS.S][valueType.Float];
            float 亮度精度 = 1 - 方法.参数[CS.V][valueType.Float];
            float 最小面积 = 方法.参数[CS.色块1面积][valueType.Float];
            int 图片压缩倍数 = 方法.参数[CS.图片压缩][valueType.Int];
            bool 取反 = 方法.参数[CS.布尔值][valueType.Bool];
            bool 找所有颜色 = 方法.参数[CS.找所有色][valueType.Bool];
            Point 参考坐标 = 方法.参数[CS.坐标2].opencv坐标;

            bool 找到 = false;
            Point 找到坐标 = new Point();
            Rect 找到区域 = new Rect();

            Using(截屏.图片[0, imageType.Mat], 大图 =>
            {
                // 应用图片压缩
                if (图片压缩倍数 > 1)
                {
                    大图 = 大图.Resize(new Size(大图.Width / 图片压缩倍数, 大图.Height / 图片压缩倍数));
                    找色区域 = new Rect(找色区域.X / 图片压缩倍数, 找色区域.Y / 图片压缩倍数,
                                     找色区域.Width / 图片压缩倍数, 找色区域.Height / 图片压缩倍数);
                    参考坐标 = new Point(参考坐标.X / 图片压缩倍数, 参考坐标.Y / 图片压缩倍数);
                }

                // 提取找色区域
                using Mat 区域图像 = new Mat(大图, 找色区域);

                // 转换为HSV
                using Mat hsv图像 = new Mat();
                Cv2.CvtColor(区域图像, hsv图像, ColorConversionCodes.BGR2HSV);

                // 存储每个颜色的所有找到的区域
                List<List<(Point 坐标, Rect 区域, double 距离)>> 所有颜色的色块列表 = new List<List<(Point, Rect, double)>>();

                // 第一步：为每个颜色找到所有匹配的区域
                foreach (Scalar 目标颜色 in 颜色列表)
                {
                    List<(Point 坐标, Rect 区域, double 距离)> 当前颜色的色块 = new List<(Point, Rect, double)>();

                    // 目标颜色转HSV
                    using Mat 目标颜色Mat = new Mat(1, 1, MatType.CV_8UC3, 目标颜色);
                    using Mat 目标HSVMat = new Mat();
                    Cv2.CvtColor(目标颜色Mat, 目标HSVMat, ColorConversionCodes.BGR2HSV);
                    Vec3b 目标HSV = 目标HSVMat.At<Vec3b>(0, 0);

                    // 计算颜色范围
                    int h范围 = (int)(色相精度 * 180);
                    int s范围 = (int)(饱和度精度 * 255);
                    int v范围 = (int)(亮度精度 * 255);

                    Scalar 下限 = new Scalar(
                        Math.Max(0, 目标HSV.Item0 - h范围),
                        Math.Max(0, 目标HSV.Item1 - s范围),
                        Math.Max(0, 目标HSV.Item2 - v范围)
                    );
                    Scalar 上限 = new Scalar(
                        Math.Min(180, 目标HSV.Item0 + h范围),
                        Math.Min(255, 目标HSV.Item1 + s范围),
                        Math.Min(255, 目标HSV.Item2 + v范围)
                    );

                    // 创建掩码
                    using Mat 颜色掩码 = new Mat();
                    Cv2.InRange(hsv图像, 下限, 上限, 颜色掩码);

                    // 查找轮廓
                    Point[][] 轮廓数组;
                    HierarchyIndex[] 层级数组;
                    Cv2.FindContours(颜色掩码, out 轮廓数组, out 层级数组, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

                    // 收集所有符合条件的轮廓
                    foreach (var 轮廓 in 轮廓数组)
                    {
                        double 轮廓面积 = Cv2.ContourArea(轮廓);
                        if (轮廓面积 >= 最小面积)
                        {
                            Rect 边界矩形 = Cv2.BoundingRect(轮廓);
                            Point 色块中心 = new Point(
                                边界矩形.X + 边界矩形.Width / 2 + 找色区域.X,
                                边界矩形.Y + 边界矩形.Height / 2 + 找色区域.Y
                            );

                            double 距离 = Math.Sqrt(Math.Pow(色块中心.X - 参考坐标.X, 2) + Math.Pow(色块中心.Y - 参考坐标.Y, 2));

                            当前颜色的色块.Add((色块中心, 边界矩形, 距离));
                        }
                    }

                    所有颜色的色块列表.Add(当前颜色的色块);
                }

                // 第二步：根据模式选择结果
                if (找所有颜色)
                {
                    // 找所有颜色：检查是否每个颜色都至少有一个区域
                    bool 所有颜色都存在 = 所有颜色的色块列表.All(色块列表 => 色块列表.Count > 0);

                    if (所有颜色都存在)
                    {
                        // 找到离参考点最近的那个颜色区域（从所有颜色中选一个最近的）
                        var 所有色块 = 所有颜色的色块列表.SelectMany(x => x);
                        if (所有色块.Any())
                        {
                            var 最近色块 = 所有色块.OrderBy(x => x.距离).First();
                            找到坐标 = 最近色块.坐标;
                            找到区域 = 最近色块.区域;
                            找到 = true;
                        }
                    }
                }
                else
                {
                    // 找任意颜色：找到离参考点最近的颜色区域
                    var 所有色块 = 所有颜色的色块列表.SelectMany(x => x);
                    if (所有色块.Any())
                    {
                        var 最近色块 = 所有色块.OrderBy(x => x.距离).First();
                        找到坐标 = 最近色块.坐标;
                        找到区域 = 最近色块.区域;
                        找到 = true;
                    }
                }

                // 坐标还原
                if (找到 && 图片压缩倍数 > 1)
                {
                    找到坐标 = new Point(找到坐标.X * 图片压缩倍数, 找到坐标.Y * 图片压缩倍数);
                    找到区域 = new Rect(
                        找到区域.X * 图片压缩倍数,
                        找到区域.Y * 图片压缩倍数,
                        找到区域.Width * 图片压缩倍数,
                        找到区域.Height * 图片压缩倍数
                    );
                }
            });

            // 结果处理
            if (找到 && !取反)
            {
                方法.参数[CS.坐标].值.赋值($"{找到坐标.X},{找到坐标.Y}");
                方法.参数[CS.目标区域].值.赋值($"{找到区域.X},{找到区域.Y},{找到区域.Width},{找到区域.Height}");
                方法data.循环num = 1;
            }
            else if (!找到 && 取反)
            {
                方法data.循环num = 1;
            }
            else
            {
                方法data.循环num = 0;
            }
        }












    }

    public class 图片显示窗体 : Form
    {
        // Win32 API
        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll")]
        private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

        // 常量
        private const int GWL_EXSTYLE = -20;
        private const int WS_EX_LAYERED = 0x00080000;
        private const int WS_EX_TRANSPARENT = 0x00000020;
        private const int LWA_ALPHA = 0x00000002;
        private const int LWA_COLORKEY = 0x00000001;

        // 控件
        private PictureBox pictureBox;
        private System.Windows.Forms.Timer 刷新定时器;

        // 图片数据
        private Mat _当前图片;
        private readonly object _图片锁 = new object();
        private bool _需要更新显示 = false;

        // 单例实例
        public static 图片显示窗体 实例 { get; private set; }

        public static void 初始化()
        {
            if (实例 == null || 实例.IsDisposed)
            {
                实例 = new 图片显示窗体();
            }
        }

        public static Form 显示窗体()
        {
            初始化();
            实例.Show();
            实例.BringToFront();
            return 实例;
        }

        public static void 隐藏窗体()
        {
            实例?.Hide();
        }

        public static void 设置图片(Mat 图片)
        {
            if (实例 == null || 实例.IsDisposed)
                初始化();

            实例.设置显示图片(图片);
        }

        public static void 清空图片()
        {
            实例?.清空显示图片();
        }

        public static void 关闭窗体()
        {
            实例?.Close();
            实例?.Dispose();
            实例 = null;
        }

        private 图片显示窗体()
        {
            InitializeComponent();
            设置窗口样式();
        }

        private void InitializeComponent()
        {
            SuspendLayout();

            // PictureBox
            pictureBox = new PictureBox
            {
                Dock = DockStyle.Fill,
                SizeMode = PictureBoxSizeMode.Zoom,
                BackColor = Color.Black,
                BorderStyle = BorderStyle.FixedSingle
            };

            // 窗体设置
            Text = "实时图片显示";
            ClientSize = new System.Drawing.Size(500, 400);
            Controls.Add(pictureBox);
            FormBorderStyle = FormBorderStyle.Sizable; // 可调整大小
            StartPosition = FormStartPosition.Manual;
            Location = new System.Drawing.Point(50, 50); // 屏幕左上角偏移一些
            TopMost = true;
            ShowInTaskbar = true;
            DoubleBuffered = true;

            // 事件
            Load += 窗体加载;
            FormClosing += 窗体关闭;

            // 刷新定时器
            刷新定时器 = new System.Windows.Forms.Timer
            {
                Interval = 33 // 约30fps
            };
            刷新定时器.Tick += 定时刷新;
            刷新定时器.Start();

            ResumeLayout(false);
        }

        private void 设置窗口样式()
        {
            // 确保窗口句柄已创建
            if (!IsHandleCreated)
                CreateHandle();

            int 样式 = GetWindowLong(Handle, GWL_EXSTYLE);
            SetWindowLong(Handle, GWL_EXSTYLE, 样式 | WS_EX_LAYERED);
            SetLayeredWindowAttributes(Handle, 0, 255, LWA_ALPHA);
        }

        private void 窗体加载(object sender, EventArgs e)
        {
            // 窗口加载后的初始化
            设置窗口样式();
        }

        private void 窗体关闭(object sender, FormClosingEventArgs e)
        {
            // 清理资源
            刷新定时器?.Stop();
            刷新定时器?.Dispose();

            lock (_图片锁)
            {
                _当前图片?.Dispose();
                _当前图片 = null;
            }

            if (pictureBox.Image != null)
            {
                pictureBox.Image.Dispose();
                pictureBox.Image = null;
            }
        }

        private void 设置显示图片(Mat 图片)
        {
            lock (_图片锁)
            {
                // 释放旧图片
                _当前图片?.Dispose();

                if (图片 != null && !图片.Empty())
                {
                    // 克隆新图片
                    _当前图片 = 图片.Clone();
                    _需要更新显示 = true;
                }
                else
                {
                    _当前图片 = null;
                    _需要更新显示 = true;
                }
            }
        }

        private void 清空显示图片()
        {
            lock (_图片锁)
            {
                _当前图片?.Dispose();
                _当前图片 = null;
                _需要更新显示 = true;
            }
        }

        private void 定时刷新(object sender, EventArgs e)
        {
            if (!_需要更新显示) return;

            lock (_图片锁)
            {
                _需要更新显示 = false;

                if (_当前图片 == null || _当前图片.Empty())
                {
                    清空PictureBox();
                    return;
                }

                try
                {
                    // 调整图片大小以适应显示
                    using (var 显示图片 = 调整图片大小(_当前图片, pictureBox.Width, pictureBox.Height))
                    {
                        if (!显示图片.Empty())
                        {
                            更新PictureBox(显示图片);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"显示图片时出错: {ex.Message}");
                    清空PictureBox();
                }
            }
        }

        private Mat 调整图片大小(Mat 原图, int 目标宽度, int 目标高度)
        {
            if (原图.Width <= 目标宽度 && 原图.Height <= 目标高度)
                return 原图.Clone();

            double 缩放比例 = Math.Min(
                (double)目标宽度 / 原图.Width,
                (double)目标高度 / 原图.Height
            );

            Mat 缩放后 = new Mat();
            Cv2.Resize(原图, 缩放后, new OpenCvSharp.Size(), 缩放比例, 缩放比例, InterpolationFlags.Linear);
            return 缩放后;
        }

        private void 更新PictureBox(Mat 图像)
        {
            if (pictureBox.InvokeRequired)
            {
                pictureBox.BeginInvoke(new Action<Mat>(更新PictureBox), 图像);
                return;
            }

            try
            {
                using (var bitmap = BitmapConverter.ToBitmap(图像))
                {
                    var 旧图片 = pictureBox.Image;
                    pictureBox.Image = (Bitmap)bitmap.Clone();
                    旧图片?.Dispose();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"更新PictureBox错误: {ex.Message}");
            }
        }

        private void 清空PictureBox()
        {
            if (pictureBox.InvokeRequired)
            {
                pictureBox.BeginInvoke(new Action(清空PictureBox));
                return;
            }

            if (pictureBox.Image != null)
            {
                pictureBox.Image.Dispose();
                pictureBox.Image = null;
            }
        }

        // 窗体控制方法
        public void 设置位置大小(int x, int y, int 宽度, int 高度)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action<int, int, int, int>(设置位置大小), x, y, 宽度, 高度);
                return;
            }

            Location = new System.Drawing.Point(x, y);
            Size = new System.Drawing.Size(宽度, 高度);
        }

        public void 设置始终置顶(bool 置顶)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action<bool>(设置始终置顶), 置顶);
                return;
            }

            TopMost = 置顶;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                刷新定时器?.Dispose();
                lock (_图片锁)
                {
                    _当前图片?.Dispose();
                }
                清空PictureBox();
            }
            base.Dispose(disposing);
        }
    }

    // 简化调用接口
    public static class 图片显示器
    {
        public static Form 显示图片(Mat 图片)
        {
            图片显示窗体.设置图片(图片);
            return 图片显示窗体.显示窗体();
        }

        public static Form 显示图片在位置(Mat 图片, int x, int y, int 宽度 = 500, int 高度 = 400)
        {
            图片显示窗体.设置图片(图片);
            图片显示窗体.实例?.设置位置大小(x, y, 宽度, 高度);
            return 图片显示窗体.显示窗体();
        }

        public static void 隐藏()
        {
            图片显示窗体.隐藏窗体();
        }

        public static void 清空()
        {
            图片显示窗体.清空图片();
        }

        public static void 关闭()
        {
            图片显示窗体.关闭窗体();
        }

        public static void 设置置顶(bool 置顶 = true)
        {
            图片显示窗体.实例?.设置始终置顶(置顶);
        }
    }
}
